package com.qiuling.iot.utils;

import com.alibaba.fastjson.JSONObject;
import com.qiuling.iot.Exception.BusinessException;
import com.qiuling.iot.Exception.code.BaseResponseCode;
import com.qiuling.iot.Exception.code.FunctionCodeEnum;
import com.qiuling.iot.constants.Constant;
import com.qiuling.iot.entity.ClientSocket;
import com.qiuling.iot.entity.SocketClient;
import com.qiuling.iot.vo.req.ClientSendReqVO;
import com.qiuling.iot.vo.req.ServerSendReqVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.concurrent.*;

/**
 * @Author：zhangqiang
 * @Description：
 * @Date：Created in 20:04 2020/2/29
 * @Modified By：
 */
@Component
@Slf4j
public class SocketClientUtil {
    /**
     * 全局缓存，用于存储已存在的socket客户端连接
     */
    public static ConcurrentMap<String, ClientSocket> existSocketClientMap = new ConcurrentHashMap<>();

    @Resource(name = "clientTaskPool")
    private ThreadPoolTaskExecutor clientExecutor;

    @Resource(name = "clientMessageTaskPool")
    private ThreadPoolTaskExecutor messageExecutor;


    public void startOneClient(String equipmentCode) {
        if (existSocketClientMap.containsKey(equipmentCode)) {
            log.info(String.format("设备：%S重复连接", equipmentCode));
        } else {
            //异步创建socket
            clientExecutor.execute(() -> {
                //新建一个socket连接
                SocketClient client;
                try {
                    /**
                     * 测试用，上线后用下面的获取用户实际ip和端口号
                     */
                    client = new SocketClient(InetAddress.getByName("127.0.0.1"), 60000);
                    //client = new SocketClient(InetAddress.getByName(ip), port);
                } catch (UnknownHostException e) {
                    throw new BusinessException(BaseResponseCode.SOCKET_CREATE_ERROR);
                }
                client.setLastOnTime(new Date());
                log.info(String.format("设备：%S已连接", equipmentCode));
                /**
                 * 创建心跳定时任务
                 */
                ScheduledExecutorService clientHeartExecutor = Executors
                        .newSingleThreadScheduledExecutor(r -> new Thread(r, "socket_client_heart_" + r.hashCode()));
                ClientSocket clientSocket = new ClientSocket(client,clientHeartExecutor);
                //连接
                ClientSendReqVO vo = new ClientSendReqVO();
                vo.setFunctionCode(FunctionCodeEnum.LOGIN.getValue());
                vo.setEquipmentCode(equipmentCode);
                client.println(JSONObject.toJSONString(vo));
                /**
                 * 接收消息处理
                 */
                messageExecutor.submit(() -> {
                    try {
                        String message;
                        while ((message = client.readLine()) != null) {
                            log.info("客户端:{}，获得消息：{}", equipmentCode, message);
                            ServerSendReqVO serverSendReqVO;
                            try {
                                serverSendReqVO = JSONObject.parseObject(message, ServerSendReqVO.class);
                            } catch (Exception e) {
                                ClientSendReqVO sendReqVO = new ClientSendReqVO();
                                sendReqVO.setFunctionCode(FunctionCodeEnum.MESSAGE.getValue());
                                sendReqVO.setMessage("data error");
                                client.println(JSONObject.toJSONString(sendReqVO));
                                break;
                            }
                            Integer functionCode = serverSendReqVO.getFunctionCode();
                            if (functionCode.equals(FunctionCodeEnum.HEART.getValue())) {
                                //心跳类型
                                client.setLastOnTime(new Date());
                            }
                        }
                    } catch (Exception e) {
                        log.error("客户端异常,equipmentCode:{},exception：{}", equipmentCode, e.getMessage());
                        client.close();
                        existSocketClientMap.remove(equipmentCode);
                    }
                });
                /**
                 * 发送心跳信息
                 */
                clientHeartExecutor.scheduleWithFixedDelay(() -> {
                    try {

                        Date lastOnTime = client.getLastOnTime();
                        long heartDuration = (new Date()).getTime() - lastOnTime.getTime();
                        if (heartDuration > Constant.HEART_RATE) {
                            //心跳超时,关闭当前线程
                            log.error("心跳超时");
                            throw new Exception("服务端已断开socket");
                        }
                        ClientSendReqVO heartVO = new ClientSendReqVO();
                        heartVO.setFunctionCode(FunctionCodeEnum.HEART.getValue());
                        /**
                         * 开始向服务器发送心跳信息
                         */
                        client.println(JSONObject.toJSONString(heartVO));
                    } catch (Exception e) {
                        log.error("客户端异常,equipmentCode:{},exception：{}", equipmentCode, e.getMessage());
                        client.close();
                        existSocketClientMap.remove(equipmentCode);
                        clientHeartExecutor.shutdown();
                    }

                }, 0, 5, TimeUnit.SECONDS);
                //绑定客户端的设备
                existSocketClientMap.put(equipmentCode, clientSocket);
            });
        }
    }


    public void closeOneClient(String equipmentCode) {
        if (!existSocketClientMap.containsKey(equipmentCode)) {
            throw new BusinessException(BaseResponseCode.EQUIPMENT_NOT_EXIST);
        }
        ClientSocket clientSocket = existSocketClientMap.get(equipmentCode);
        clientSocket.getClientHeartExecutor().shutdown();
        clientSocket.getSocketClient().close();
        existSocketClientMap.remove(equipmentCode);
    }
}
